/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "arch/asm_support.h"
#include "arch/asm_support.h"
#include "arch/amd64/helpers_amd64.S"

// extern "C" uint64_t InvokeBuiltinHandleException(JSThread *thread, JSTaggedValue retval)
.extern InvokeBuiltinHandleException

// CompiledCodeToBuiltinBridge, follows DynamicMethod calling convention
.global CompiledCodeToBuiltinBridge
.type CompiledCodeToBuiltinBridge, %function
CompiledCodeToBuiltinBridge:
    CFI_STARTPROC
    CFI_DEF_CFA(rsp, 8)

    // setup frame
    pushq %rbp
    CFI_ADJUST_CFA_OFFSET(8)
    CFI_REL_OFFSET(rbp, 0)

    movq %rsp, %rbp
    CFI_DEF_CFA_REGISTER(rbp)

    pushq %rdi
    pushq $CFRAME_KIND_NATIVE // fill flags slot

    movq %rbp, MANAGED_THREAD_FRAME_OFFSET(%THREAD_REG)
    movb $1, MANAGED_THREAD_FRAME_KIND_OFFSET(%THREAD_REG)
    // rsp must be 16 bytes aligned

    // Skip locals
    subq $(CFRAME_LOCALS_COUNT * 8), %rsp

    // save all the callee saved registers to the stack
    // stack walker will read them during stack unwinding
    pushq %THREAD_REG
    CFI_REL_OFFSET(THREAD_REG, -((CFRAME_CALLEE_REGS_START_SLOT + 0) * 8))
    pushq %r14
    CFI_REL_OFFSET(r14, -((CFRAME_CALLEE_REGS_START_SLOT + 1) * 8))
    pushq %r13
    CFI_REL_OFFSET(r13, -((CFRAME_CALLEE_REGS_START_SLOT + 2) * 8))
    pushq %r12
    CFI_REL_OFFSET(r12, -((CFRAME_CALLEE_REGS_START_SLOT + 3) * 8))
    pushq %rbx
    CFI_REL_OFFSET(rbx, -((CFRAME_CALLEE_REGS_START_SLOT + 4) * 8))

    // save arguments to the stack
    subq $(7 * 8), %rsp // 6 caller gp + alignment
    movq %rdi, (1 * 8)(%rsp)
    movq %rsi, (2 * 8)(%rsp)

    leaq 16(%rbp), %r10 // pointer to stack_args

    subq $(4 * 8), %rsp
    movq %THREAD_REG, (0 * 8)(%rsp)
    movl %esi,        (1 * 8)(%rsp)
    movq %r10,        (2 * 8)(%rsp)

    movq METHOD_NATIVE_POINTER_OFFSET(%rdi), %r10
    movq %rsp, %rdi
    callq *%r10

    // TODO(vpukhov): check ret instead
    movq MANAGED_THREAD_EXCEPTION_OFFSET(%THREAD_REG), %rdi
    test %rdi, %rdi
    jne .Lhandle_exception
1:

    // Restore callee registers, since GC may change its values while moving objects.
    movq -((CFRAME_CALLEE_REGS_START_SLOT + 0) * 8)(%rbp), %r15
    CFI_RESTORE(r15)
    movq -((CFRAME_CALLEE_REGS_START_SLOT + 1) * 8)(%rbp), %r14
    CFI_RESTORE(r14)
    movq -((CFRAME_CALLEE_REGS_START_SLOT + 2) * 8)(%rbp), %r13
    CFI_RESTORE(r13)
    movq -((CFRAME_CALLEE_REGS_START_SLOT + 3) * 8)(%rbp), %r12
    CFI_RESTORE(r12)
    movq -((CFRAME_CALLEE_REGS_START_SLOT + 4) * 8)(%rbp), %rbx
    CFI_RESTORE(rbx)

    movq %rbp, %rsp
    popq %rbp
    CFI_RESTORE(rbp)
    CFI_DEF_CFA(rsp, (1 * 8))
    retq
    CFI_ENDPROC

.Lhandle_exception:
    movq %rax, %rsi
    movq %THREAD_REG, %rdi
    callq InvokeBuiltinHandleException@plt
    jmp 1b
